package datatypes.list;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.ListIterator;

public class DoublyLinkedList<E> implements List<E> {
	private int size = 0;
	Node<E> current = null;
	Node<E> head = null;
	Node<E> tail = null;
	private List<Node<E>> nodes = new ArrayList<Node<E>>();
	private List<E> elements = new ArrayList<E>();

	@Override
	public boolean add(E element) {
		Node<E> newNode = new Node<E>(element);
		if (head == null) {
			current = newNode;
			head = newNode;
			tail = newNode;
		} else {
			newNode.previous = tail;
			tail.next = newNode;
			tail = newNode;
			if (current == null) {
				current = newNode;
			}
		}
		nodes.add(newNode);
		elements.add(element);
		size++;
		return true;
	}
	
	public void moveToHead() {
		current = head;
	}
		
	public Node<E> headNode() {
		return head;
	}
	
	public E peek() {
		if (current == null) {
			return null;
		}
		return current.element;
	}
	
	public boolean hasNext() {
		return current != null;
	}
	
	public E next() {
		return nextNode().element;
	}
	
	public Node<E> nextNode() {
		if (current == null) {
			return null;
		}
		Node<E> node = current;
		current = current.next;
		return node;
	}
	
	public boolean hasPrevious() {
		return !(current == null || current.previous == null);
	}
	
	public E previous() {
		if ( ! hasPrevious() ) {
			return null;
		}
		current = current.previous;
		return current.element;
	}

	@Override
	public void add(int arg0, E arg1) {
		throw new UnsupportedOperationException();
	}

	@Override
	public boolean addAll(Collection<? extends E> arg0) {
		throw new UnsupportedOperationException();
//		return false;
	}

	@Override
	public boolean addAll(int arg0, Collection<? extends E> arg1) {
		throw new UnsupportedOperationException();
//		return false;
	}

	@Override
	public void clear() {
		throw new UnsupportedOperationException();
//		current = null;
//		head = null;
//		tail = null;
//		size = 0;
	}

	@Override
	public boolean contains(Object o) {
		if (o instanceof Node<?>) {
			return nodes.contains(o);
		}
		return elements.contains(o);
	}

	@Override
	public boolean containsAll(Collection<?> objects) {
		Iterator<?> iterator = objects.iterator();
		if (iterator.hasNext() && iterator.next() instanceof Node<?>) {
			return nodes.containsAll(objects);
		}
		return elements.containsAll(objects);
	}

	/**
	 * returns the Element of the node at the specified index.
	 * WARNING: the indices do not necessarily reflect the
	 * order of this list!!!
	 */
	@Override
	public E get(int index) {
		return elements.get(index);
	}

	@Override
	public int indexOf(Object o) {
		if (o instanceof Node<?>) {
			return nodes.indexOf(o);
		}
		return elements.indexOf(o);
	}

	@Override
	public boolean isEmpty() {
		return size == 0;
	}

	@Override
	public Iterator<E> iterator() {
		DoublyLinkedListIterator<E> iterator = new DoublyLinkedListIterator<E>(head);
		return iterator;
	}
	
	public Iterable<Node<E>> nodeIterable() {
		DoublyLinkedListNodeIterator<E> iterator = new DoublyLinkedListNodeIterator<E>(head);
		return iterator;
	}

	@Override
	public int lastIndexOf(Object arg0) {
		throw new UnsupportedOperationException();
//		return 0;
	}

	@Override
	public ListIterator<E> listIterator() {
		throw new UnsupportedOperationException();
//		return null;
	}

	@Override
	public ListIterator<E> listIterator(int index) {
		throw new UnsupportedOperationException();
//		return null;
	}
	
	public ListIterator<Node<E>> listNodeIterator(int index) {
		return nodes.listIterator(index);
	}
	
	@Override
	public boolean remove(Object o) {
		int index = indexOf(o);
		if (index < 0) {
			return false;
		}
		Node<E> toBeRemoved = nodes.remove(index);
		elements.remove(index);
		return removeNode(toBeRemoved);
	}
	
	private boolean removeNode(Node<E> node) {
		if (node == current) {
			current = current.next;
		}
		if (node == head) {
			head = head.next;
		}
		if (node == tail) {
			tail = tail.previous;
		}
		node.remove();
		
		size--;
		return true;
	}

	@Override
	public E remove(int index) {
		Node<E> toBeRemoved = nodes.remove(index);
		elements.remove(index);
		remove(toBeRemoved);
		return toBeRemoved.element;
	}

	@Override
	public boolean removeAll(Collection<?> arg0) {
		throw new UnsupportedOperationException();
//		return false;
	}

	@Override
	public boolean retainAll(Collection<?> arg0) {
		throw new UnsupportedOperationException();
//		return false;
	}

	@Override
	public E set(int arg0, E arg1) {
		throw new UnsupportedOperationException();
//		return null;
	}

	@Override
	public int size() {
		return size;
	}

	@Override
	public List<E> subList(int arg0, int arg1) {
		throw new UnsupportedOperationException();
//		return null;
	}

	@Override
	public Object[] toArray() {
		return nodes.toArray();
	}

	@Override
	public <T> T[] toArray(T[] arg0) {
		return nodes.toArray(arg0);
	}

}
